在完成 在 Chapter 1 中,我們靠需求驅動,一步步寫出一支可完整點單、送單、統計的應用。但隨著功能增加,單一檔案的程式碼開始膨脹:
修改一小段功能就得翻找整份檔案
不同區塊邏輯彼此糾纏,維護困難
這正是推導出「組件化
」的最佳時機:
我們不是因為大家都說: 一定要學組件化
才去study去學,而是因為實際需求與痛點驅使
為什麼會這樣說?
在大型的應用程式當中,需要將表單、清單、統計等拆成可重複利用
、可獨立維護
的元件否則你一直重複寫同樣的按鈕或是彈跳視窗不是會很幹XDD!!
然而,想要邁向更大型、更維護良好的專案,需要 元件化拆分、API 串接、狀態管理 與 表單驗證
等更進階的工程能力。
本章將從「如何拆分元件」開始,一步步帶大家完成更結構化的 Vue 應用,並建立與後端溝通與可維護的資料流。
我們chapter2系列的內容是這樣
基本上還是會因為需求驅動來學習這些咚咚~
Day | 標題 | 核心學習內容 |
---|---|---|
Day 7 | 如何把複雜的咒語變簡單:組件化設計 | 將龐大的 App.vue 拆成 OrderForm (下單表單)、OrderList (訂單清單)、OrderStats (統計表)三大元件。學會 Props / Emit 的父子元件溝通,理解組件化帶來的維護性與重複利用價值。 |
Day 8 | 讓你接受我的控制:受控表單設計 | 以 受控元件 管理輸入狀態,示範重置、暫存、回填等功能,優化表單 UX 。 |
Day 9 | 連接世界的魔法:API 溝通術 | 介紹 fetch / axios 與 Service 層封裝,學習處理 loading 、error 狀態,打通與後端溝通的資料管道。 |
Day 10 | 立即小助手:v-watch |
使用 watch 監聽資料變化,動態更新甜度、冰量等客製化選單;並討論 快取策略 與 效能優化 。 |
Day 11 | 中央魔島書院:共享的 store |
以 Pinia store 集中管理訂單狀態,示範跨頁面共享資料與 模組化維護 的做法。 |
Day 12 | 可靠的中央行政單位:getter / setter |
把 Day 5 的統計邏輯重構到 Pinia getters ,集中衍生資料與計算,降低元件負擔。 |
Day 13 | 驗證自己的咒語語法:表單與資料驗證 | 使用 VeeValidate + Yup 完成 必填 、格式檢查 與即時提示,確保輸入資料的正確與安全。 |
我們第二章的章節會照這樣的學習路徑執行
凡是從觀察跟需求開始
我們觀察 Day1~Day5 的畫面:飲料 / 甜度 / 冰量 這三塊 UI其實是同一種結構,只是標題與選項文案不同。
與其在頁面上複製三份幾乎一樣的 HTML,不如抽出一個通用組件(OptionGroup):
傳入 legend(群組標題)
傳入 options(選項陣列)
今天先做純展示版(stateless / presentational):
不含 v-model / @change / Pinia
只渲染三組清單,為 Day7 的「組件化 + 互動」打地基
今天我們算是前導篇章
所以程式flow比較簡單,只是要把組件的概念具象化
所以流程是長這樣
props 是子元件的接收參數。父元件可將資料以屬性方式傳入,子元件透過 defineProps 宣告要接收哪些資料
其實可以不用想這麼複雜,你就想這邊的子組件是誰? 就是我們需要重複利用的OptionGroup.vue
例如我們要傳飲料的屬性傳下去
先在父祖件引入oprtiongroup,這時候就可以直接使用 組件
那麼其中的屬性跟標籤就可以透過綁定屬性來傳下去
在父祖件中我可以把drink的state綁起來
傳下去,這個叫做option(飲料的選項)
<!-- 父元件 App.vue -->
<OptionGroup :options="drinks" legend="選擇飲料" />
在子組件中,我可以透過defineprops定義物件結構
把要接受父親的屬性型別定義出來(比如說我們飲料有紅茶跟綠茶陣列)
<!-- 子元件 OptionGroup.vue -->
<script setup>
const props = defineProps({
legend: String,
options: Array
})
</script>
我們所需要的資料
const drinks = ['紅茶', '綠茶']
const sweetness = ['正常甜', '去糖']
const iceLevels = ['正常冰', '去冰']
在template中,我們引入Optiongroup就可以重複利用這個組件
<OptionGroup legend="步驟 1:選擇飲料" :options="drinks" />
<OptionGroup legend="步驟 2:選擇甜度" :options="sweetness" />
<OptionGroup legend="步驟 3:選擇冰量" :options="iceLevels" />
這樣我們就可以接收
上面飲料的選項/甜度冰量等客製化了
<script setup>
const props = defineProps({
legend: String,
options: Array
})
</script>
<template>
<fieldset>
<legend>{{ props.legend }}</legend>
<ul>
<li v-for="opt in props.options" :key="opt">{{ opt }}</li>
</ul>
</fieldset>
</template>
我們把今天的程式碼結合就可以變成
父組件
<!-- App.vue -->
<script setup>
import OptionGroup from './OptionGroup.vue'
const drinks = ['紅茶', '綠茶']
const sweetness = ['正常甜', '去糖']
const iceLevels = ['正常冰', '去冰']
</script>
<template>
<h2>Chapter 2:組件化暖身(純展示)</h2>
<!-- 三個清單:今天只做 UI 呈現,不處理狀態與事件 -->
<OptionGroup legend="步驟 1:選擇飲料" :options="drinks" name="drink-demo" />
<OptionGroup legend="步驟 2:選擇甜度" :options="sweetness" name="sweet-demo" />
<OptionGroup legend="步驟 3:選擇冰量" :options="iceLevels" name="ice-demo" />
</template>
子組件
<!-- OptionGroup.vue -->
<script setup>
const props = defineProps({
legend: { type: String, required: true },
options: { type: Array, required: true }, // 例:['紅茶', '綠茶']
name: { type: String, required: true }, // for radio 的群組名(純展示可用或忽略)
})
</script>
<template>
<fieldset>
<legend class="title">{{ legend }}</legend>
<ul class="list">
<li v-for="opt in options" :key="opt" class="item">
<label><input type="radio" :name="name" :value="opt" disabled /> {{ opt }}</label>
</li>
</ul>
</fieldset>
</template>
可以從這邊試玩
Chapter2 的組件前導
結果:父元件的 drinks / sweetness / iceLevels 陣列,就會經由 props 傳到 OptionGroup,由子元件渲染出完整的選單 UI。
今天我們可以先有一點概念,明天就會開始正式拆解我們的組件瞜~!!